﻿using System;
using System.Collections.Generic;
using System.Linq;
using CCAI.Office.PowerPoint.TimedText.Captions;

namespace CCAI.Office.PowerPoint.TimedText
{
    public class MediaMarkerCollection<TMediaMarker> : OrderedCollection<TMediaMarker> where TMediaMarker : MediaMarker
    {
        private readonly IDictionary<string, TMediaMarker> _itemHash;

        private bool _suspendCollectionChangedEvents;

        public MediaMarkerCollection()
        {
            _itemHash = new Dictionary<string, TMediaMarker>();
        }

        public event Action<TMediaMarker> MarkerPositionChanged;

        public new void Add(TMediaMarker mediaMarker)
        {
            if (!_itemHash.ContainsKey(mediaMarker.Id))
            {
                var comparable = new MediaMarkerComparable(mediaMarker.Begin);
                Add(mediaMarker, comparable);
            }
        }

        public bool ContainsId(string id)
        {
            return _itemHash.ContainsKey(id);
        }

        public TMediaMarker GetById(string id)
        {
            return _itemHash[id];
        }

        public IEnumerable<TMediaMarker> WhereActiveAtPosition(TimeSpan mediaPosition, TimeSpan? searchAfter = null)
        {
            var startIndex = 0;

            if (searchAfter.HasValue)
            {
                var comparer = new MediaMarkerComparable(searchAfter.Value);
                startIndex = SearchForInsertIndex(comparer);
            }

            return this.Skip(startIndex)
                .TakeWhile(i => i.Begin <= mediaPosition)
                .Where(i => i.IsActiveAtPosition(mediaPosition))
                .ToList();
        }

        public IEnumerable<TMediaMarker> WhereActiveInRange(TimeSpan rangeStart, TimeSpan rangeEnd,
                                                            TimeSpan? searchAfter = null)
        {
            var startIndex = 0;

            if (searchAfter.HasValue)
            {
                var comparer = new MediaMarkerComparable(searchAfter.Value);
                startIndex = SearchForInsertIndex(comparer);
            }

            return this.Skip(startIndex)
                .TakeWhile(i => i.Begin <= rangeEnd)
                .Where(i => i.IsActiveInRange(rangeStart, rangeEnd))
                .ToList();
        }

        public IEnumerable<TMediaMarker> WhereContainedByRange(TimeSpan rangeStart, TimeSpan rangeEnd)
        {
            var comparer = new MediaMarkerComparable(rangeStart);
            var startIndex = SearchForInsertIndex(comparer);

            return this.Skip(startIndex).TakeWhile(i => i.IsContainedByRange(rangeStart, rangeEnd));
        }

        public new void Clear()
        {
            _itemHash.Clear();
            base.Clear();
        }


        //       protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        //       {
        //           if (e.NewItems != null)
        //           {
        //               e.NewItems.Cast<TMediaMarker>()
        //                         .ForEach(OnItemAdded);
        //           }

        //           if (e.OldItems != null)
        //           {
        //               e.OldItems.Cast<TMediaMarker>()
        //                         .ForEach(OnItemRemoved);
        //           }

        //           if (!_suspendCollectionChangedEvents)
        //           {
        ////note               base.OnCollectionChanged(e);
        //           }
        //       }

        private void OnItemAdded(TMediaMarker item)
        {
            _itemHash.Add(item.Id, item);
            item.PositionChanged += Item_PositionChanged;
        }

        private void OnItemRemoved(TMediaMarker item)
        {
            item.PositionChanged -= Item_PositionChanged;
            _itemHash.Remove(item.Id);
        }

        private void Item_PositionChanged(MediaMarker item)
        {
            var tItem = item as TMediaMarker;

            if (tItem != null)
            {
                _suspendCollectionChangedEvents = true;
                Remove(tItem);
                Add(tItem);
                _suspendCollectionChangedEvents = false;
                MarkerPositionChanged.IfNotNull(i => i(tItem));
            }
        }

        #region Nested type: MediaMarkerComparable

        private class MediaMarkerComparable : IComparable<TMediaMarker>
        {
            private TimeSpan _searchTime;

            public MediaMarkerComparable(TimeSpan beginTime)
            {
                _searchTime = beginTime;
            }

            #region IComparable<TMediaMarker> Members

            public int CompareTo(TMediaMarker other)
            {
                return _searchTime.CompareTo(other.Begin);
            }

            #endregion
        }

        #endregion
    }
}